package com.drug.common.utils.valid;

import com.alibaba.fastjson.JSON;
import com.drug.common.annotation.CheckParam;
import com.drug.common.constant.HttpStatus;
import com.drug.common.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @author Alexlyn
 * @Date 2021/12/10 10:00
 * @Description: 参数校验工具类
 * @Version 1.0
 */
@Slf4j
public class ParamCheckUtils {


    private static final String StringClass = "class java.lang.String";
    private static final String ListClass = "interface java.util.List";
    private static final String wmsVoClass = "com.drug.wms.domain";
    private static final String baseEntityClass = "com.drug.common.core.domain";


    /**
     * 参数校验
     *
     * @param obj       对象obj
     * @param superFlag 是否校验父类
     */
    public static void validate(Object obj, boolean superFlag) {
        long validateStart = System.currentTimeMillis();
        log.info("参数校验: " + JSON.toJSON(obj));

        // 避免空指针异常
        if (obj == null) {
            return;
        }
        Class<?> _class = obj.getClass();
        int _count = isValidObj(obj, _class, 0);
        if (_class != null && superFlag) {
            // 父类参数校验
            isValidObj(obj, _class.getSuperclass(), _count);
        }
        log.info("validate耗时=:" + (System.currentTimeMillis() - validateStart) + "ms");
    }

    /**
     * @param obj    对象obj
     * @param _count 递归校验次数
     * @return 1
     */
    private static int isValidObj(Object obj, int _count) {
        // 避免空指针异常
        if (obj == null) {
            return _count;
        }
        return isValidObj(obj, obj.getClass(), _count);
    }

    /**
     * @param obj    对象obj
     * @param _class 对象class
     * @param _count 递归校验次数
     * @return 1
     */
    private static int isValidObj(Object obj, Class<?> _class, int _count) {
        log.info("obj.getClass():" + JSON.toJSON(_class));
        // 递归校验次数限制
        if (_count > 100) {
            log.info("validate-count-limit:" + _count);
            return _count;
        }
        if (obj != null && _class != null) {
            if (_class.getName().startsWith(wmsVoClass) || _class.getName().startsWith(baseEntityClass)) {
                _count++; // 递归校验次数+1
                log.info("validate-count:" + _count);
                // getDeclaredFields：获得某个类的所有声明字段，包括public、private、protected，但是不包括父类的声明字段
                Field[] fields = _class.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(CheckParam.class)) {
                        _count = isValidParam(obj, field, _count);
                    }
                }
            }
        }
        return _count;
    }

    /**
     * 校验注解方法入口
     *
     * @param obj    对象obj
     * @param field  属性
     * @param _count 递归校验次数
     * @return 1
     */
    private static int isValidParam(Object obj, Field field, int _count) {
        CheckParam CheckParam = field.getAnnotation(CheckParam.class);
        log.info("getType: " + field.getName() + ":" + field.getType().toString());

        if (field.getGenericType().toString().equals(StringClass)) {
            // String属性
            isValidString(obj, field, CheckParam);
        } else if (field.getType().toString().equals(ListClass)) {
            // List属性
            _count = isValidList(obj, field, CheckParam, _count);
        } else {
            // 其余属性
            _count = isValidOther(obj, field, CheckParam, _count);
        }
        return _count;
    }

    /**
     * String对象校验
     *
     * @param obj        对象obj
     * @param field      属性
     * @param checkParam 校验规则
     */
    private static void isValidString(Object obj, Field field, CheckParam checkParam) {
        String fieldVal = getValInvoke(obj, field.getName());
        // 校验是否允许为空
        if (!checkParam.isNull()) {
            if (StringUtils.isBlank(fieldVal)) {
                throwException(checkParam, ":不能为空");
            }
        }
        // 避免空指针异常
        if (StringUtils.isBlank(fieldVal)) {
            log.info("空对象:String");
            return;
        }

//        int valLength = fieldVal.getBytes(StandardCharsets.UTF_8).length;
        int valLength = fieldVal.length();

        // 校验固定长度
        if (checkParam.fixLength() != -1) {
            if (checkParam.fixLength() != valLength) {
                throwException(checkParam, ":长度有误");
            }
        }
        // 校验最大长度
        if (checkParam.maxLength() != -1) {
            if (valLength > checkParam.maxLength()) {
                throwException(checkParam, ":最大长度有误");
            }
        }
        // 校验最小长度
        if (checkParam.minLength() != -1) {
            if (valLength < checkParam.minLength()) {
                throwException(checkParam, ":最小长度有误");
            }
        }
        // 校验字符内容
        if (StringUtils.isNotEmpty(checkParam.pattern())) {
            if (!Pattern.matches(checkParam.pattern(), fieldVal)) {
                throwException(checkParam, ":数据内容有误");
            }
        }
    }

    /**
     * List对象校验
     *
     * @param obj        对象obj
     * @param field      属性
     * @param checkParam 校验规则
     * @param _count     递归校验次数
     * @return 1
     */
    private static int isValidList(Object obj, Field field, CheckParam checkParam, int _count) {
        List<?> objList = getValInvoke(obj, field.getName());
        // 校验是否允许为空
        if (!checkParam.isNull()) {
            if (objList == null || objList.size() == 0) {
                throwException(checkParam, ":数据内容不能为空");
            }
        }

        // 避免空指针异常
        if (objList == null) {
            log.info("空对象:objList");
            return _count;
        }

        // 列表参数校验
        for (Object ob : objList) {
            _count = isValidObj(ob, _count);
        }
        return _count;
    }

    /**
     * @param obj        对象obj
     * @param field      属性
     * @param checkParam 校验规则
     * @param _count     递归校验次数
     * @return 1
     */
    private static int isValidOther(Object obj, Field field, CheckParam checkParam, int _count) {
        Object objRec = getValInvoke(obj, field.getName());
        // 校验是否允许为空
        if (!checkParam.isNull()) {
            if (objRec == null) {
                throwException(checkParam, ":数据内容不能为空");
            }
        }

        // 避免空指针异常
        if (objRec == null) {
            log.info("空对象:objRec");
            return _count;
        }

        return isValidObj(objRec, _count);
    }

    /**
     * 反射获取对象字段值
     *
     * @param obj       对象obj
     * @param fieldName 属性
     * @return 1
     */
    @SuppressWarnings("unchecked")
    private static <T> T getValInvoke(Object obj, String fieldName) {
        try {
            Method m = obj.getClass().getMethod("get" + getMethodName(fieldName));
            return (T) m.invoke(obj);
        } catch (Exception e) {
            log.error(":调用getter方法获取属性值异常", e);
        }
        return null;
    }

    /**
     * 字符串首字母大写
     *
     * @param filedName 属性
     * @return 1
     */
    private static String getMethodName(String filedName) {
        byte[] items = filedName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }

    private static void throwException(CheckParam v, String errText) {
        throw new CommonException(v.message() + " " + errText, HttpStatus.BAD_REQUEST);
    }
}
